<?php
/**
 * @package qBase
 * @subpackage web
 */

/**
 * QWebModule is a standard QIWebListener implementer that pushes the request to the next element in the call stack
 * 
 * @package qBase
 * @subpackage web
 */
class QWebModule extends QObject implements QIWebListener 
{
	/**
	 * The listeners that are part of this module, specified as class names
	 *
	 * @var array
	 */
	protected $listeners;
	/**
	 * The default listener to trigger in case none is defined
	 *
	 * @var string
	 */
	protected $default_listener = null;
	/**
	 * The base href prefered by the module, the QIWebListener that will be processed may override this value
	 *
	 * @var string
	 */
	protected $base_href;
	/**
	 * The listener that has been triggered by the module
	 *
	 * @var QIWebListener
	 */
	protected $running_listener;
	/**
	 * The URL manager of the module
	 *
	 * @var QUrlManager
	 */
	protected $url_manager = null;
	/**
	 * The parent module or app
	 *
	 * @var QWebModule
	 */
	protected $parent;
	/**
	 * The request URL relative to BASE HREF
	 *
	 * @var string
	 */
	protected $request_url = null;
	/**
	 * The dafault res path of the module
	 *
	 * @var string
	 */
	protected $default_res_path = null;

	/**
	 * The constructor
	 *
	 */
	public function __construct()
	{
		parent::__construct();

		// $this->debug("construct : module :" . get_class($this));
	}
	
	/**
	 * Gets the listener that has been started by this module
	 *
	 * @return QIWebListener
	 */
	public function getRunningListener()
	{
		return $this->running_listener;
	}
	
	/**
	 * Gets the running web page
	 *
	 * @return QWebPage
	 */
	public function getRunningPage()
	{
		$module = $this;
		while ($module)
		{
			if (!$module->running_listener)
				return null;
			else if ($module->running_listener instanceof QWebPage)
				return $module->running_listener;
			else if ($module->running_listener instanceof QWebModule)
				return $module->running_listener->getRunningPage();
			else 
				return null;
		}
	}
	
	/**
	 * Returns the base href preffered by the module, the QIWebListener that will be processed may override this value
	 *
	 * @return string
	 */
	public function getBaseHref()
	{
		return $this->base_href;
	}

	/**
	 * Processes the request, by either calling the next QIWebListener in the call stack or calling the default QIWebListener
	 *
	 * @param QWebRequest $request
	 * @param QIWebListener $parent
	 */
	public function processRequest(QWebRequest $request, QIWebListener $parent = null)
	{
		if ($parent && ($parent != $this))
		{
			if ($parent instanceof QWebModule)
				$this->parent = $parent;
			else 
				throw new Exception("The parent of a QWebModule must be a instance of QWebModule");
		}
		if ((!$this->base_href) && (($parent instanceof QWebApp) || ($parent instanceof QWebModule) || 
			($parent instanceof QWebPage)))
		{
			$this->base_href = $parent->getBaseHref();
		}
		if ((!$this->request_url) && (($parent instanceof QWebApp) || ($parent instanceof QWebModule) || ($parent instanceof QWebPage)))
		{
			$this->request_url = $parent->getRequestUrl();
		}

		$current_call_stack = QWebContext::GetCurrentCallStack();
		QWebContext::IncrementCallStackPosition();

		if (!$current_call_stack)
		{
			// use default
			if (!$this->default_listener)
				throw new Exception(get_class($this)."::processRequest no default listener was specified");
			else
			{
				// VERY IMPORTANT ! SECURITY RELATED
				if (!QObject::IsA($this->default_listener, "QObject"))
					throw new Exception("QWebModule::processRequest() this->default_listener must be an object");
				eval("\$wl = new {$this->default_listener}(); \$this->running_listener = \$wl; \$wl->processRequest(\$request, \$this);");
			}
		}
		else
		{
			// $this->debug("CS : " . $current_call_stack);
			// VERY IMPORTANT ! SECURITY RELATED

			if ((!in_array($current_call_stack, $this->listeners)) && ($current_call_stack != $this->getClassName()))
				throw new Exception("QWebModule::processRequest() the object in the request stack ('{$current_call_stack}') must be added as listener to the web module ({$this->getClassName()})");
			if (!QObject::IsA($current_call_stack, "QObject"))
				throw new Exception("QWebModule::processRequest() the object in the request stack ('{$current_call_stack}') must be an object of QObject ({$this->getClassName()})");
			// call the listener if in list
			eval("\$wl = new {$current_call_stack}(); \$this->running_listener = \$wl; \$wl->processRequest(\$request, \$this);");
		}

	}
	
	/**
	 * Adds a listener to the module
	 *
	 * @param string $class_name
	 */
	public function addListener($class_name, $set_default = false)
	{
		if (!$this->listeners)
			$this->listeners = array();

		$this->listeners[] = $class_name;

		if ($set_default)
			$this->setDefaultListener($class_name);
	}
	
	/**
	 * Removes a listener from the module, returns true if the class name has been found and removed
	 *
	 * @param string $class_name
	 * @return boolean
	 */
	public function removeListener($class_name)
	{
		if (!$this->listeners)
			return false;
			
		$pos = array_search($class_name, $this->listeners, true);
		
		if ($pos !== false)
		{
			array_splice($this->listeners, $pos, 1);
			return true;
		}
		else 
			return false;
	}
	
	/**
	 * Removes all listeners from this module
	 *
	 */
	public function removeAllListeners()
	{
		if ($this->listeners)
			unset($this->listeners);
	}
	
	/**
	 * Tests if this listener contains the specified listener
	 *
	 * @param string $class_name
	 * @return boolean
	 */
	public function hasListener($class_name)
	{
		if (!$this->listeners)
			return false;
			
		$pos = array_search($class_name, $this->listeners, true);
		
		if ($pos !== false)
			return true;
		else 
			return false;
	}
	
	/**
	 * Gets the default listener for this module
	 *
	 * @return string
	 */
	public function getDefaultListener()
	{
		return $this->default_listener;
	}
	
	/**
	 * Sets the default listener for the module
	 *
	 * @param string $listener
	 */
	public function setDefaultListener($listener)
	{
		$this->default_listener = $listener;
	}
	
	/**
	 * Gets the Url manager of the module
	 *
	 * @return QUrlManager
	 */
	public function getUrlManager()
	{
		// $this->debug("getUrlManager : " , $this->url_manager);
		if ($this->url_manager)
			return $this->url_manager;
		else if ($this->parent)
			return $this->parent->getUrlManager();
		return null;
	}
	
	/**
	 * Sets the Url manager of the module
	 *
	 * @param QUrlManager $url_manager
	 */
	public function setUrlManager(QUrlManager $url_manager)
	{
		$this->url_manager = $url_manager;
		// $this->debug("setUrlManager : " . get_class($this->url_manager));
	}
	
	public function getRequestUrl()
	{
		return $this->request_url;
	}
	
	/**
	 * Gets the parent QIWebListener
	 *
	 * @return QIWebListener
	 */
	public function getParentWebListener()
	{
		return $this->parent;
	}
	/**
	 * Gets the call stack of the current QIWebListener as a string array
	 * 
	 * @param $skip_defaults If true will not put in the array the QIWebListener(s) that are the default listeners of their parent
	 *
	 * @return array
	 */
	public function getCallStack($skip_defaults = false)
	{
		$call_stack = array();
		$list = $this;
		
		while ($list != null)
		{
			$parent = $list->getParentWebListener();
			$class = $list->getClassName();
			
			if (!$parent)
			{
				// no need to include objects that are instance of QWebApp
				if (!($list instanceof QWebApp))
					$call_stack[] = $class;
				// end of line
				break;
			}
			
			if ((!$skip_defaults) || (!(($parent instanceof QWebModule) && ($parent->getDefaultListener() == $class))))
				$call_stack[] = $class;
			// else // has the same class as the default listener, no need to add it to the call stack
			
			$list = $parent;
		}
	}
	
	/**
	 * Sets the default resource path for QWebControl(s)
	 *
	 * @param string $path
	 */
	public function setDefaultResourcePath($path, $relative_to_running_folder = false)
	{
		if ($relative_to_running_folder)
			$this->default_res_path = QWebApp::RelativePath($path, QWebApp::GetRunningFolder());
		else
			$this->default_res_path = $path;
	}
	
	/**
	 * Gets the default resource path for QWebControl(s)
	 *
	 * @return string
	 */
	public function getDefaultResourcePath()
	{
		return $this->default_res_path;
	}
	
	public function getIdWebListener()
	{
		return $this->getClassName();
	}
}

?>